/**
 * Config Validator
 * Validates main configuration objects
 */

const BaseValidator = require('./base-validator');
const ProviderValidator = require('./provider-validator');

class ConfigValidator extends BaseValidator {
  constructor(options = {}) {
    super('config', {
      strict: false,
      testMode: false,
      allowExtraFields: true,
      ...options
    });

    this.providerValidator = new ProviderValidator(options);
  }

  /**
   * Perform configuration validation
   * @param {Object} config - Configuration object
   * @param {Object} context - Validation context
   * @returns {Object} Validation result
   */
  performValidation(config, context = {}) {
    if (!config || typeof config !== 'object') {
      return this.failure('Configuration must be a valid object', [{
        field: 'config',
        message: 'Must be an object',
        value: config
      }]);
    }

    const errors = [];
    const sanitized = { ...config };

    // Validate required fields
    const requiredFields = ['version', 'current_provider_model', 'providers'];
    for (const field of requiredFields) {
      if (!this.hasField(config, field)) {
        errors.push({
          field,
          message: `Required field '${field}' is missing`,
          value: config[field]
        });
      }
    }

    // Validate version
    if (config.version) {
      const versionValidation = this.validateVersion(config.version);
      if (!versionValidation.valid) {
        errors.push(...versionValidation.errors);
      } else {
        sanitized.version = versionValidation.value;
      }
    }

    // Validate current_provider_model
    if (config.current_provider_model) {
      const currentModelValidation = this.validateCurrentProviderModel(config.current_provider_model);
      if (!currentModelValidation.valid) {
        errors.push(...currentModelValidation.errors);
      } else {
        sanitized.current_provider_model = currentModelValidation.value;
      }
    }

    // Validate providers
    if (config.providers) {
      const providersValidation = this.validateProviders(config.providers);
      if (!providersValidation.valid) {
        errors.push(...providersValidation.errors);
      } else {
        sanitized.providers = providersValidation.value;
      }
    }

    // Validate optional sections
    const optionalSections = ['http_server', 'logging', 'mcp', 'features'];
    for (const section of optionalSections) {
      if (config[section] !== undefined) {
        const sectionValidation = this.validateOptionalSection(section, config[section]);
        if (!sectionValidation.valid) {
          errors.push(...sectionValidation.errors);
        } else {
          sanitized[section] = sectionValidation.value;
        }
      }
    }

    // Cross-validation
    const crossValidation = this.validateCrossDependencies(sanitized);
    if (!crossValidation.valid) {
      errors.push(...crossValidation.errors);
    }

    if (errors.length > 0) {
      return this.failure('Configuration validation failed', errors);
    }

    return this.success(sanitized);
  }

  /**
   * Check if object has field
   * @param {Object} obj - Object to check
   * @param {string} field - Field name
   * @returns {boolean} Whether field exists
   */
  hasField(obj, field) {
    return obj && obj.hasOwnProperty(field) && obj[field] !== undefined && obj[field] !== null;
  }

  /**
   * Validate version field
   * @param {string} version - Version string
   * @returns {Object} Validation result
   */
  validateVersion(version) {
    if (typeof version !== 'string') {
      return {
        valid: false,
        errors: [{
          field: 'version',
          message: 'Version must be a string',
          value: version
        }]
      };
    }

    // In test mode, allow more flexible versions
    if (this.options.testMode) {
      if (version === 'test' || version.startsWith('test-')) {
        return { valid: true, value: version };
      }
    }

    // Validate semantic version pattern
    const versionPattern = /^\d+\.\d+\.\d+$/;
    if (!versionPattern.test(version)) {
      return {
        valid: false,
        errors: [{
          field: 'version',
          message: 'Version must follow semantic versioning (e.g., "1.0.0")',
          value: version
        }]
      };
    }

    return { valid: true, value: version };
  }

  /**
   * Validate current provider model
   * @param {string} currentModel - Current provider model
   * @returns {Object} Validation result
   */
  validateCurrentProviderModel(currentModel) {
    if (typeof currentModel !== 'string') {
      return {
        valid: false,
        errors: [{
          field: 'current_provider_model',
          message: 'Current provider model must be a string',
          value: currentModel
        }]
      };
    }

    if (currentModel.trim() === '') {
      return {
        valid: false,
        errors: [{
          field: 'current_provider_model',
          message: 'Current provider model cannot be empty',
          value: currentModel
        }]
      };
    }

    return { valid: true, value: currentModel };
  }

  /**
   * Validate providers object
   * @param {Object} providers - Providers configuration
   * @returns {Object} Validation result
   */
  validateProviders(providers) {
    if (typeof providers !== 'object' || providers === null || Array.isArray(providers)) {
      return {
        valid: false,
        errors: [{
          field: 'providers',
          message: 'Providers must be an object',
          value: providers
        }]
      };
    }

    const errors = [];
    const sanitizedProviders = {};
    const providerKeys = Object.keys(providers);

    if (providerKeys.length === 0) {
      return {
        valid: false,
        errors: [{
          field: 'providers',
          message: 'At least one provider must be configured',
          value: providers
        }]
      };
    }

    for (const [providerName, providerConfig] of Object.entries(providers)) {
      // Validate provider name
      if (!/^[a-zA-Z][a-zA-Z0-9_-]*$/.test(providerName)) {
        errors.push({
          field: `providers.${providerName}`,
          message: 'Provider name must start with a letter and contain only letters, numbers, hyphens, and underscores',
          value: providerName
        });
        continue;
      }

      // Validate provider configuration
      const providerValidation = this.providerValidator.validate(providerConfig, {
        providerName,
        parentConfig: providers
      });

      if (!providerValidation.success) {
        // Add provider context to errors
        const contextErrors = providerValidation.errors.map(error => ({
          ...error,
          field: `providers.${providerName}.${error.field}`
        }));
        errors.push(...contextErrors);
      } else {
        sanitizedProviders[providerName] = providerValidation.data;
      }
    }

    if (errors.length > 0) {
      return {
        valid: false,
        errors
      };
    }

    return { valid: true, value: sanitizedProviders };
  }

  /**
   * Validate optional configuration section
   * @param {string} sectionName - Section name
   * @param {*} sectionConfig - Section configuration
   * @returns {Object} Validation result
   */
  validateOptionalSection(sectionName, sectionConfig) {
    if (typeof sectionConfig !== 'object' || sectionConfig === null || Array.isArray(sectionConfig)) {
      return {
        valid: false,
        errors: [{
          field: sectionName,
          message: `${sectionName} must be an object`,
          value: sectionConfig
        }]
      };
    }

    // Basic validation - in non-strict mode, we allow most optional sections
    switch (sectionName) {
      case 'http_server':
        return this.validateHttpServer(sectionConfig);
      case 'logging':
        return this.validateLogging(sectionConfig);
      case 'mcp':
        return this.validateMcp(sectionConfig);
      case 'features':
        return this.validateFeatures(sectionConfig);
      default:
        // Unknown section, allow in non-strict mode
        return { valid: true, value: sectionConfig };
    }
  }

  /**
   * Validate HTTP server configuration
   * @param {Object} httpConfig - HTTP server config
   * @returns {Object} Validation result
   */
  validateHttpServer(httpConfig) {
    const errors = [];
    const sanitized = { ...httpConfig };

    if (httpConfig.port !== undefined) {
      if (typeof httpConfig.port !== 'number' || httpConfig.port < 1024 || httpConfig.port > 65535) {
        errors.push({
          field: 'http_server.port',
          message: 'Port must be a number between 1024 and 65535',
          value: httpConfig.port
        });
      }
    }

    if (httpConfig.auto_start !== undefined && typeof httpConfig.auto_start !== 'boolean') {
      errors.push({
        field: 'http_server.auto_start',
        message: 'Auto start must be a boolean',
        value: httpConfig.auto_start
      });
    }

    if (httpConfig.host !== undefined) {
      if (typeof httpConfig.host !== 'string' || httpConfig.host.trim() === '') {
        errors.push({
          field: 'http_server.host',
          message: 'Host must be a non-empty string',
          value: httpConfig.host
        });
      }
    }

    if (errors.length > 0) {
      return {
        valid: false,
        errors
      };
    }

    return { valid: true, value: sanitized };
  }

  /**
   * Validate logging configuration
   * @param {Object} loggingConfig - Logging config
   * @returns {Object} Validation result
   */
  validateLogging(loggingConfig) {
    const errors = [];
    const sanitized = { ...loggingConfig };

    if (loggingConfig.level !== undefined) {
      const validLevels = ['error', 'warn', 'info', 'debug'];
      if (!validLevels.includes(loggingConfig.level)) {
        errors.push({
          field: 'logging.level',
          message: `Level must be one of: ${validLevels.join(', ')}`,
          value: loggingConfig.level
        });
      }
    }

    if (loggingConfig.retention_days !== undefined) {
      if (typeof loggingConfig.retention_days !== 'number' || loggingConfig.retention_days < 1 || loggingConfig.retention_days > 365) {
        errors.push({
          field: 'logging.retention_days',
          message: 'Retention days must be a number between 1 and 365',
          value: loggingConfig.retention_days
        });
      }
    }

    if (errors.length > 0) {
      return {
        valid: false,
        errors
      };
    }

    return { valid: true, value: sanitized };
  }

  /**
   * Validate MCP configuration
   * @param {Object} mcpConfig - MCP config
   * @returns {Object} Validation result
   */
  validateMcp(mcpConfig) {
    const errors = [];
    const sanitized = { ...mcpConfig };

    if (mcpConfig.server_name !== undefined) {
      if (typeof mcpConfig.server_name !== 'string' || mcpConfig.server_name.trim() === '') {
        errors.push({
          field: 'mcp.server_name',
          message: 'Server name must be a non-empty string',
          value: mcpConfig.server_name
        });
      }
    }

    if (mcpConfig.timeout !== undefined) {
      if (typeof mcpConfig.timeout !== 'number' || mcpConfig.timeout < 1000 || mcpConfig.timeout > 300000) {
        errors.push({
          field: 'mcp.timeout',
          message: 'Timeout must be a number between 1000 and 300000',
          value: mcpConfig.timeout
        });
      }
    }

    if (errors.length > 0) {
      return {
        valid: false,
        errors
      };
    }

    return { valid: true, value: sanitized };
  }

  /**
   * Validate features configuration
   * @param {Object} featuresConfig - Features config
   * @returns {Object} Validation result
   */
  validateFeatures(featuresConfig) {
    const errors = [];
    const sanitized = { ...featuresConfig };

    // Check boolean fields
    const booleanFields = ['auto_model_selection', 'cache_analysis', 'debug_mode'];
    for (const field of booleanFields) {
      if (featuresConfig[field] !== undefined && typeof featuresConfig[field] !== 'boolean') {
        errors.push({
          field: `features.${field}`,
          message: `${field} must be a boolean`,
          value: featuresConfig[field]
        });
      }
    }

    if (errors.length > 0) {
      return {
        valid: false,
        errors
      };
    }

    return { valid: true, value: sanitized };
  }

  /**
   * Validate cross-dependencies between configuration sections
   * @param {Object} config - Sanitized configuration
   * @returns {Object} Validation result
   */
  validateCrossDependencies(config) {
    const errors = [];

    // Check if current_provider_model exists in providers
    if (config.current_provider_model && config.providers) {
      // In test mode, be more lenient with model validation
      if (this.options.testMode) {
        // Accept any reasonable test pattern
        const testPatterns = [
          /^test-[a-zA-Z0-9_-]+$/,           // test-model-name
          /^[a-zA-Z]+-[a-zA-Z0-9_-]+$/,      // provider-model format
          /^test-[a-zA-Z]+-[a-zA-Z0-9_-]+$/, // test-provider-model format
        ];

        const isTestPattern = testPatterns.some(pattern => pattern.test(config.current_provider_model));
        if (isTestPattern) {
          return { valid: true };
        }

        // Also accept provider-model format even if the specific model doesn't exist yet
        if (config.current_provider_model.includes('-')) {
          const [providerName] = config.current_provider_model.split('-');
          if (config.providers[providerName]) {
            return { valid: true };
          }
        }
      }

      const modelExists = Object.entries(config.providers).some(([providerName, provider]) => {
        if (provider.models && Array.isArray(provider.models)) {
          return provider.models.some(model => {
            // Extract model identifier for comparison - prioritize ID over name
            let modelId;
            let modelName;
            if (typeof model === 'string') {
              modelId = model;
              modelName = model;
            } else if (model && typeof model === 'object') {
              modelId = model.id;
              modelName = model.name;
            } else {
              return false;
            }

            // Check both ID and name for matching
            const modelMatchesId = modelId === config.current_provider_model;
            const modelMatchesName = modelName === config.current_provider_model;
            const providerModelMatchesId = `${providerName}-${modelId}` === config.current_provider_model;
            const providerModelMatchesName = `${providerName}-${modelName}` === config.current_provider_model;

            // Also check if current_provider_model is just the model identifier without provider prefix
            const simpleModelMatch = modelId === config.current_provider_model || modelName === config.current_provider_model;

            return (modelMatchesId || modelMatchesName || providerModelMatchesId || providerModelMatchesName || simpleModelMatch) &&
                   (typeof model === 'string' ? true : (model.enabled !== false));
          });
        }
        return false;
      });

      if (!modelExists) {
        errors.push({
          field: 'current_provider_model',
          message: 'Current provider model must exist and be enabled in providers configuration',
          value: config.current_provider_model
        });
      }
    }

    return {
      valid: errors.length === 0,
      errors: errors.length > 0 ? errors : undefined
    };
  }

  /**
   * Create a test-friendly configuration
   * @param {Object} overrides - Configuration overrides
   * @returns {Object} Test configuration
   */
  createTestConfig(overrides = {}) {
    return {
      version: '1.0.0',
      current_provider_model: 'test-test-model',
      providers: {
        test: this.providerValidator.createTestConfig()
      },
      http_server: {
        port: 3000,
        auto_start: false,
        host: 'localhost'
      },
      logging: {
        level: 'info',
        retention_days: 7,
        file_enabled: false,
        console_enabled: true
      },
      mcp: {
        server_name: 'test-vision-analyzer',
        server_version: '1.0.0',
        timeout: 5000
      },
      features: {
        auto_model_selection: true,
        cache_analysis: false,
        debug_mode: true
      },
      ...overrides
    };
  }

  /**
   * Get suggestion for configuration validation error
   * @param {Object} error - Validation error
   * @returns {string} Suggestion message
   */
  getSuggestion(error) {
    switch (error.field) {
      case 'version':
        return 'Provide a semantic version (e.g., "1.0.0"). For testing, use "test"';
      case 'current_provider_model':
        return 'Ensure the current provider model exists in the providers configuration';
      case 'providers':
        return 'Configure at least one provider with valid API settings';
      default:
        if (error.field.startsWith('providers.')) {
          return 'Check the provider configuration for correct format and required fields';
        }
        return `Check the ${error.field} field for correct format and value`;
    }
  }
}

module.exports = ConfigValidator;